﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Timers;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Windows.Forms;

    /// <summary>
    /// Equalization class for Equalization operation
    /// </summary>
    public class Equalization
    {
        /// <summary>
        /// histogram object
        /// </summary>
        private static Histogram histogramObj = new Histogram();

        /// <summary>
        /// red color values
        /// </summary>
        private static double[] red = new double[256];

        /// <summary>
        /// green color values
        /// </summary>
        private static double[] green = new double[256];

        /// <summary>
        /// blue color values
        /// </summary>
        private static double[] blue = new double[256];

        /// <summary>
        /// maxred maxdray max blue max green
        /// </summary>
        private static double maxValueR = 0, maxValueG = 0, maxValueB = 0;

        /// <summary>
        /// Histograms the equalization.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>new image</returns>
        public static Bitmap HistogramEqualization(Bitmap image)
        {
            // DateTime before = DateTime.Now;

            // ImageStructure[,] newImage = new ImageStructure[image.Height, image.Width];
            int width = image.Width;
            int height = image.Height;

            Bitmap bmpNew = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            Bitmap bmpOld = image;


            unsafe
            {
                BitmapData bmpNewD = bmpNew.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                BitmapData bmpOldD = bmpOld.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int originalExtraBytes = bmpOldD.Stride - (width * 3);
                int originalHeight = height;
                int originalWidth = width;

                double[] redEqul = new double[256];
                double[] greenEqul = new double[256];
                double[] blueEqul = new double[256];

                double[] redHisto = new double[256];
                double[] greenHisto = new double[256];
                double[] blueHisto = new double[256];

                int i, j, originalPixelIndex;
                // int chunk = 100;
                byte* pOriginal = (byte*)bmpOldD.Scan0;
                byte* pNew = (byte*)bmpNewD.Scan0;



                //fill arry histogram
                for (i = 0; i < originalHeight; i++)
                {
                    for (j = 0; j < originalWidth; j++)
                    {
                        originalPixelIndex = (i * originalWidth * 3) + (i * originalExtraBytes) + (j * 3);
                        redHisto[pOriginal[originalPixelIndex]]++;
                        greenHisto[pOriginal[originalPixelIndex + 1]]++;
                        blueHisto[pOriginal[originalPixelIndex + 2]]++;
                    }
                }

                redEqul[0] = redHisto[0];
                blueEqul[0] = blueHisto[0];
                greenEqul[0] = greenHisto[0];
                //for accumlated sum
                for (i = 1; i < 256; i++)
                {
                    redEqul[i] = redEqul[i - 1] + redHisto[i];

                    blueEqul[i] = blueEqul[i - 1] + blueHisto[i];

                    greenEqul[i] = greenEqul[i - 1] + greenHisto[i];
                }

                double largeRed = redEqul[255];
                double largeBlue = blueEqul[255];
                double largeGreen = greenEqul[255];
                //divided
                for (i = 0; i < 256; i++)
                {
                    redEqul[i] = redEqul[i] / largeRed;
                    greenEqul[i] = greenEqul[i] / largeGreen;
                    blueEqul[i] = blueEqul[i] / largeBlue;
                }

                //Multiply
                for (i = 0; i < 256; i++)
                {
                    redEqul[i] = redEqul[i] * 255; // maxValueR;
                    blueEqul[i] = blueEqul[i] * 255; // maxValueB;
                    greenEqul[i] = greenEqul[i] * 255; // maxValueG;
                }

                //round
                for (i = 0; i < 256; i++)
                {
                    redEqul[i] = Math.Floor(redEqul[i]);
                    greenEqul[i] = Math.Floor(greenEqul[i]);
                    blueEqul[i] = Math.Floor(blueEqul[i]);
                }

                //move in new buffer
                for (i = 0; i < originalHeight; i++)
                {
                    for (j = 0; j < originalWidth; j++)
                    {
                        originalPixelIndex = (i * originalWidth * 3) + (i * originalExtraBytes) + (j * 3);

                        pNew[originalPixelIndex] = (byte)(redEqul[pOriginal[originalPixelIndex]]);
                        pNew[originalPixelIndex + 1] = (byte)(greenEqul[pOriginal[originalPixelIndex + 1]]);
                        pNew[originalPixelIndex + 2] = (byte)(blueEqul[pOriginal[originalPixelIndex + 2]]);
                    }
                }

                bmpNew.UnlockBits(bmpNewD);
                bmpOld.UnlockBits(bmpOldD);
                // TimeSpan diff = DateTime.Now - before;
                // MessageBox.Show(diff.TotalMilliseconds.ToString());
                //  newImage = Operations.ConvertBitmapToStructure(bmpNew);

            }
            return bmpNew;
        }


        /// <summary>
        /// Gets the eqlization.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="redPar">The red par.</param>
        /// <param name="greenPar">The green par.</param>
        /// <param name="bluePar">The blue par.</param>
        public static void GetEqlization(DImage image, double[] redPar, double[] greenPar, double[] bluePar)
        {
            // histogramObj.FillallArray(image);

            // histogramObj.FillRedArray(image);
            // histogramObj.FillBlueArray(image);
            // histogramObj.FillGreenArray(image);
            // histogramObj.FillGrayArray(image);
            FillHistogramData(image);

            // FillallSum(image);
            FillallDivide();
            FillMaxValue(image);
            FillallMultiply();

            for (int i = 0; i < 256; i++)
            {
                redPar[i] = Math.Round(red[i]);
                greenPar[i] = Math.Round(green[i]);
                bluePar[i] = Math.Round(blue[i]);
            }
        }

        public static void geteqlizationtest(Bitmap image1, int addressOriginal, int originalExtraBytes1, double[] red, double[] green, double[] blue)
        {

            int i, j, originalPixelIndex;
            int width = image1.Width;
            int height = image1.Height;
            unsafe
            {
                byte* pOriginal = (byte*)addressOriginal;

                //eqlization
                double[] redHisto = new double[256];
                double[] greenHisto = new double[256];
                double[] blueHisto = new double[256];
                //fill arry histogram
                for (i = 0; i < height; i++)
                {
                    for (j = 0; j < width; j++)
                    {
                        originalPixelIndex = (i * width * 3) + (i * originalExtraBytes1) + (j * 3);
                        redHisto[(int)pOriginal[originalPixelIndex]]++;
                        greenHisto[(int)pOriginal[originalPixelIndex + 1]]++;
                        blueHisto[(int)pOriginal[originalPixelIndex + 2]]++;
                    }
                }

                red[0] = redHisto[0];
                blue[0] = blueHisto[0];
                green[0] = greenHisto[0];
                //for accumlated sum
                for (i = 1; i < 256; i++)
                {
                    red[i] = red[i - 1] + redHisto[i];
                    blue[i] = blue[i - 1] + blueHisto[i];
                    green[i] = green[i - 1] + greenHisto[i];
                }

                double largeRed = red[255];
                double largeBlue = blue[255];
                double largeGreen = green[255];
                //divided
                for (i = 0; i < 256; i++)
                {
                    red[i] = red[i] / largeRed;
                    green[i] = green[i] / largeGreen;
                    blue[i] = blue[i] / largeBlue;
                }

                //Multiply
                for (i = 0; i < 256; i++)
                {
                    red[i] = red[i] * 255; // maxValueR;
                    blue[i] = blue[i] * 255; // maxValueB;
                    green[i] = green[i] * 255; // maxValueG;
                }

                //round
                for (i = 0; i < 256; i++)
                {
                    red[i] = Math.Floor(red[i]);
                    green[i] = Math.Floor(green[i]);
                    blue[i] = Math.Floor(blue[i]);
                }
            }

        }

        /// <summary>
        /// Histograms the matching.
        /// </summary>
        /// <param name="image1">The image1.</param>
        /// <param name="image2">The image2.</param>
        /// <returns>the new match</returns>
        public static Bitmap HistogramMatching(Bitmap image1, Bitmap image2)
        {
            double[] red1 = new double[256];
            double[] green1 = new double[256];
            double[] blue1 = new double[256];

            double[] red2 = new double[256];
            double[] green2 = new double[256];
            double[] blue2 = new double[256];

            // ImageStructure[,] newImage = new ImageStructure[image1.Height, image1.Width];
            Bitmap bmpNew = new Bitmap(image1.Width, image1.Height, PixelFormat.Format24bppRgb);
            Bitmap bmpOld1 = image1;
            Bitmap bmpOld2 = image2;


            unsafe
            {
                BitmapData bmpNewD = bmpNew.LockBits(new Rectangle(0, 0, image1.Width, image1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                BitmapData bmpOldD1 = bmpOld1.LockBits(new Rectangle(0, 0, image1.Width, image1.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                BitmapData bmpOldD2 = bmpOld2.LockBits(new Rectangle(0, 0, image2.Width, image2.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                int originalExtraBytes1 = bmpOldD1.Stride - (image1.Width * 3);
                int originalHeight1 = image1.Height;
                int originalWidth1 = image1.Width;

                int originalExtraBytes2 = bmpOldD2.Stride - (image2.Width * 3);
                int originalHeight2 = image2.Height;
                int originalWidth2 = image2.Width;

                int i, j, originalPixelIndex;
                // int chunk = 100;

                byte* pOriginal1 = (byte*)bmpOldD1.Scan0;
                byte* pOriginal2 = (byte*)bmpOldD2.Scan0;
                byte* pNew = (byte*)bmpNewD.Scan0;

                geteqlizationtest(image1, (int)bmpOldD1.Scan0, originalExtraBytes1, red1, green1, blue1);
                geteqlizationtest(image2, (int)bmpOldD2.Scan0, originalExtraBytes2, red2, green2, blue2);

                double diffminR = 256, diffminG = 256, diffminB = 256;
                int indexMinDiffR = 0, indexMinDiffG = 0, indexMinDiffB = 0;
                double oldmatchvalueR = 0, oldmatchvalueG = 0, oldmatchvalueB = 0;

                for (i = 0; i < 256; i++)
                {
                    diffminR = 256; diffminG = 256; diffminB = 256;
                    indexMinDiffR = 0; indexMinDiffG = 0; indexMinDiffB = 0;
                    oldmatchvalueR = 0; oldmatchvalueG = 0; oldmatchvalueB = 0;
                    for (j = 0; j < 256; j++)
                    {
                        if (Math.Abs(red2[j] - red1[i]) <= diffminR)
                        {
                            diffminR = Math.Abs(red1[i] - red2[j]);
                            indexMinDiffR = j;
                            oldmatchvalueR = red2[j];
                        }

                        if (Math.Abs(green2[j] - green1[i]) <= diffminG)
                        {
                            diffminG = Math.Abs(green1[i] - green2[j]);
                            indexMinDiffG = j;
                            oldmatchvalueG = green2[j];
                        }

                        if (Math.Abs(blue2[j] - blue1[i]) <= diffminB)
                        {
                            diffminB = Math.Abs(blue1[i] - blue2[j]);
                            indexMinDiffB = j;
                            oldmatchvalueB = blue2[j];
                        }
                    }
                    red1[i] = indexMinDiffR;
                    green1[i] = indexMinDiffG;
                    blue1[i] = indexMinDiffB;
                }//end big loop

                //generat new image
                for (i = 0; i < image1.Height; i++)
                {
                    for (j = 0; j < image1.Width; j++)
                    {
                        originalPixelIndex = (i * image1.Width * 3) + (i * originalExtraBytes1) + (j * 3);
                        pNew[originalPixelIndex] = (byte)(red1[pOriginal1[originalPixelIndex]]);
                        pNew[originalPixelIndex + 1] = (byte)(green1[pOriginal1[originalPixelIndex + 1]]);
                        pNew[originalPixelIndex + 2] = (byte)(blue1[pOriginal1[originalPixelIndex + 2]]);
                    }
                }

                //PHFastCpp.HistogramMatching((int)bmpOldD1.Scan0, (int)bmpOldD2.Scan0, (int)bmpNewD.Scan0, image1.Width, image1.Height, image2.Width,
                //    image2.Height, originalExtraBytes1, originalExtraBytes2, originalExtraBytes1);
                bmpOld1.UnlockBits(bmpOldD1);
                bmpOld2.UnlockBits(bmpOldD2);
                bmpNew.UnlockBits(bmpNewD);
                //  newImage = Operations.ConvertBitmapToStructure(bmpNew);
            }

            return bmpNew;
        }

        /// <summary>
        /// Histograms the local eqlization.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="mask">The mask.</param>
        /// <returns>histogram local eqlization image</returns>
        public static ImageStructure[,] HistogramLocalEqlization(DImage image, int mask)
        {
            int height = image.Height;
            int width = image.Width;
            ImageStructure[,] maskImage = new ImageStructure[mask, mask];
            ImageStructure[,] newimage = new ImageStructure[height, width];
            DImage tempMask = new DImage();
            double[] red1 = new double[256];
            double[] green1 = new double[256];
            double[] blue1 = new double[256];

            System.Timers.Timer t1 = new System.Timers.Timer();
            System.Timers.Timer tgetlocal = new System.Timers.Timer();
            double secondt1 = 0, secondupdate = 0;

            int counterY = 0;
            int counterX = 0;
            int midellMask = (mask / 2) + 1;
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    for (int maskY = 0; maskY < mask; maskY++)
                    {
                        for (int maskX = 0; maskX < mask; maskX++)
                        {
                            counterX = x + maskX;
                            counterY = y + maskY;

                            if (counterX >= width)
                            {
                                counterX = x;
                            }

                            if (counterY >= height)
                            {
                                counterY = y;
                            }

                            maskImage[maskY, maskX] = image.ImageStructArray[counterY, counterX];
                        }
                    }

                    t1.Start();
                    tempMask.UpdateImage(maskImage);
                    t1.Stop();
                    secondt1 += t1.Interval;
                    tgetlocal.Start();

                    GetEqlizationLocal(tempMask, red1, green1, blue1);
                    tgetlocal.Stop();
                    secondupdate += tgetlocal.Interval;
                    counterX = x + midellMask;
                    counterY = y + midellMask;

                    if (counterX >= width)
                    {
                        counterX = x;
                    }

                    if (counterY >= height)
                    {
                        counterY = y;
                    }

                    image.ImageStructArray[y, x].R = Convert.ToByte(red1[image.ImageStructArray[counterY, counterX].R]);
                    image.ImageStructArray[y, x].G = Convert.ToByte(green1[image.ImageStructArray[counterY, counterX].G]);
                    image.ImageStructArray[y, x].B = Convert.ToByte(blue1[image.ImageStructArray[counterY, counterX].B]);
                }
            }

            return newimage;
        }

        /// <summary>
        /// Gets the eqlization local.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="redPar">The red par.</param>
        /// <param name="greenPar">The green par.</param>
        /// <param name="bluePar">The blue par.</param>
        public static void GetEqlizationLocal(DImage image, double[] redPar, double[] greenPar, double[] bluePar)
        {
            // histogramObj.FillRedArray(image);
            // histogramObj.FillBlueArray(image);
            // histogramObj.FillGreenArray(image);
            // histogramObj.FillGrayArray(image);
            histogramObj.FillallArray(image);
            FillallSum(image);
            FillallDivide();
            FillMaxValue(image);
            FillallMultiply();
            for (int i = 0; i < 256; i++)
            {
                redPar[i] = Math.Round(red[i]);
                greenPar[i] = Math.Round(green[i]);
                bluePar[i] = Math.Round(blue[i]);
            }
        }

        /// <summary>
        /// Fillalls the sum.
        /// </summary>
        /// <param name="image">The image.</param>
        private static void FillallSum(DImage image)
        {
            double temp = 0;
            red[0] = histogramObj.Red1[0];
            blue[0] = histogramObj.Blue1[0];
            green[0] = histogramObj.Green1[0];
            for (int i = 1; i < 256; i++)
            {
                temp = histogramObj.Red1[i];
                red[i] = red[i - 1] + temp;

                temp = histogramObj.Blue1[i];
                blue[i] = blue[i - 1] + temp;

                temp = histogramObj.Green1[i];
                green[i] = green[i - 1] + temp;
            }
        }

        /// <summary>
        /// Fill alls the divide.
        /// </summary>
        private static void FillallDivide()
        {
            double temp;
            for (int i = 0; i < 256; i++)
            {
                temp = red[i];
                red[i] = temp / red[255];
                temp = green[i];
                green[i] = temp / green[255];
                temp = blue[i];
                blue[i] = temp / blue[255];
            }
        }

        /// <summary>
        /// Fills the max value.
        /// </summary>
        /// <param name="image">The image.</param>
        private static void FillMaxValue(DImage image)
        {
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    maxValueR = image.ImageStructArray[i, j].R > maxValueR ? image.ImageStructArray[i, j].R : maxValueR;
                    maxValueG = image.ImageStructArray[i, j].G > maxValueG ? image.ImageStructArray[i, j].G : maxValueG;
                    maxValueB = image.ImageStructArray[i, j].B > maxValueB ? image.ImageStructArray[i, j].B : maxValueB;
                }
            }
        }

        /// <summary>
        /// Fillalls the multiply.
        /// </summary>
        private static void FillallMultiply()
        {
            for (int i = 0; i < 256; i++)
            {
                red[i] = red[i] * 255; // maxValueR;
                blue[i] = blue[i] * 255; // maxValueB;
                green[i] = green[i] * 255; // maxValueG;
            }
        }

        /// <summary>
        /// Gets the match pixel.
        /// </summary>
        /// <param name="arrMatch">The arr match.</param>
        /// <param name="point">The point.</param>
        /// <returns>index of match</returns>
        private static int GetMatchPixel(double[] arrMatch, double point)
        {
            double diffmin = 256;
            int indexMinDiff = 0;
            double oldmatchvalue = 0;
            for (int i = 0; i < 256; i++)
            {
                // if (arrMatch[i] == point)
                // {
                //    return i;
                // }
                if (Math.Abs(arrMatch[i] - point) <= diffmin)
                {
                    diffmin = Math.Abs(point - arrMatch[i]);
                    indexMinDiff = i;
                    oldmatchvalue = arrMatch[i];
                }
            }

            return indexMinDiff;
        }

        /// <summary>
        /// Fills the histogram data.
        /// </summary>
        /// <param name="image">The image.</param>
        private static void FillHistogramData(DImage image)
        {
            double[] red1 = new double[256];
            double[] green1 = new double[256];
            double[] blue1 = new double[256];

            red = new double[256];
            green = new double[256];
            blue = new double[256];
            for (int i = 0; i < image.Height; i++)
            {
                for (int j = 0; j < image.Width; j++)
                {
                    red1[image.ImageStructArray[i, j].R]++;
                    green1[image.ImageStructArray[i, j].G]++;
                    blue1[image.ImageStructArray[i, j].B]++;
                }
            }

            double temp = 0;
            red[0] = red1[0];
            blue[0] = blue1[0];
            green[0] = green1[0];
            for (int i = 1; i < 256; i++)
            {
                temp = red1[i];
                red[i] = red[i - 1] + temp;

                temp = blue1[i];
                blue[i] = blue[i - 1] + temp;

                temp = green1[i];
                green[i] = green[i - 1] + temp;
            }
        }
    }
}